/**
 * worker.c
 *
 *  Created on: Jan 24, 2012
 *      Author: marco
 */

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>

#include "comsock.h"
#include "dgraph.h"
#include "match.h"
#include "queue.h"
#include "worker.h"


volatile sig_atomic_t sgpipe = 0; //OK in comune?!
volatile sig_atomic_t sgn = 0;

/*Variabili globali ma locali al thread*/
__thread char *name;
__thread sigset_t set;
__thread message_t login_msg;
__thread message_t incoming;
__thread message_t outgoing;
__thread int client_sock_fd;
__thread int client_connection;
__thread user* current_user;

/*Variabili extern dichiarate in mgcars*/

/*Messaggi di risposta comuni*/
extern message_t reply_msg;
extern message_t ok_msg;
extern message_t pwd_msg;
extern message_t already_msg;
extern graph_t *map;
extern int pending_signal;
extern user_list_node *user_list;
extern queue *offers;
extern queue *requests;
extern pthread_mutex_t offers_mtx;
extern pthread_mutex_t requests_mtx;
extern pthread_mutex_t users_mtx;

//extern user_list_node *userlist;//forse inutile?

void* worker_start(void* sfd) {
	int rcv;
	int reply;
	int check;
	char *name;
	char *pwd;
	char *client_socket;

	//a sto punto fare free di sfd?
	//o mettere client sock come puntatore?!
	/*Gestione segnali*/

	struct sigaction *sa;
	client_connection = *(int*)sfd;
	sa = calloc(sizeof(struct sigaction), 1);
	/*Si mascherano tutti i segnali per il thread worker*/
	if (sigfillset(&set) == -1) {
		return ((void*) (NULL)); //mah
	}
	if (pthread_sigmask(SIG_SETMASK, &set, NULL) == -1) {
		return ((void*) NULL); //mah
	}
	memset(sa, sizeof(sa), 0);
	sa->sa_handler = SIG_IGN;
	sigaction(SIGQUIT, sa, NULL);
//EC ANCHE SU SIGACTION
	/*Handlers del thread*/
	sa->sa_handler = worker_sigpipe_handler;//rivedere
	sigaction(SIGPIPE, sa, NULL);
	free(sa);

	/*Ricezione messaggio e autenticazione client*/
	rcv = receiveMessage(client_connection, &login_msg);
	if (rcv == -1) {
		//meglio?!
		//liberare
		pthread_exit(NULL);
	}

	printf("MESSAGGIO DA CLIENT TIPO %c lungo %d\n", login_msg.type,
			login_msg.length);
	if (login_msg.type == MSG_CONNECT) {
		//MUTEX LISTA UTENTI
		printf("prima di check\n");
		//OCCHIO COMSOCK SE CLIENT SI DISCONNETTE DA -1..
		//e quindi sbombarda dopo qui
		//fare un controllo con la lunghezza?
		printf("inizio buffer !%s!\n", login_msg.buffer); //login è globale del thread e il buffer è mallocato
		name = login_msg.buffer;
		pwd = &(login_msg.buffer[strlen(name) + 1]);
		printf("pwd !%s!\n", pwd);
		client_socket = &(pwd[strlen(pwd) + 2]);
		printf("socket client !%s!\n", client_socket);

		client_sock_fd = openConnection(client_socket); //error check
		if (client_sock_fd == -1) {
			//meglio?!
			//liberare
			pthread_exit(NULL);
		}

		printf("client sock fd %d\n", client_sock_fd);
		check = user_check(name, pwd, &current_user); //check crea l'utente se nuovo
		switch (check) {

		case (LOGIN_OK): {

			printf("LOGIN OK\n");
			printf("u name !%s! pwd !%s!\n", current_user->name,
					current_user->pwd);
			// se ok..
			//si mette sfd nella struct
			current_user->sfd = client_sock_fd;
			printf("prima di send msg di %d byte\n", ok_msg.length);
			//CHIUDERE COMUNQUE LE CLIENT CONNECTION?!?!

			sendMessage(client_sock_fd, &ok_msg);
			if (errno == EPIPE) {
				current_user->sfd = 0;
				closeSocket(client_sock_fd); //QUALE CHIUDERE?!?!
				printf("AAAAAA\n");
			}
			printf("send effettuata\n");

			//ELSE?!
			//messaggio di errore, magari uno solo per tanti casi

			//SBLOCCO LISTA UTENTI

			break;
		}

		case (WRONG_PWD): {
			//SBLOCCO LISTA UTENTI
			printf("LOGIN PASS SBAGLIATA\n");
			//EC!!!!! d send
			sendMessage(client_sock_fd, &pwd_msg);
			closeSocket(client_sock_fd);
			pthread_exit(NULL);
			break;
		}

		case (ALREADY_CONN): {
			//SBLOCCO LISTA UTENTI
			printf("LOGI GIA' ONLINE\n");

			sendMessage(client_sock_fd, &already_msg);
			closeSocket(client_sock_fd);
			pthread_exit(NULL);

			break;
		}

		}

	} else {
		//che fare?!?!?!?1 msg diverso da connect
	}
	//mettere a null u a fine ciclo

	/*Ciclo di ascolto del socket*/
	printf("worker si mette ad attendere messaggii\n");
	while (pending_signal == 0) {
		printf("ciclo,prima di receive\n");
		rcv = receiveMessage(client_sock_fd, &incoming);
		printf("worker RCV %d, ricevuto TIPO %c !%s! lungo %d\n",rcv,incoming.type, incoming.buffer,
				incoming.length);

		//controllo sulla receive
		if(rcv == -1){
			printf("WORKER: errore receive thread in uscita\n");
			perror("worker receive");
			worker_stop();
		}


		switch (incoming.type) {

		case (MSG_EXIT): {
			worker_stop();
			break;
		}

		case (MSG_OFFER): {
			reply = offer(incoming.buffer);
			break;
		}

		case (MSG_REQUEST): {
			reply = request(incoming.buffer);
			break;
		}

		}//fine switch tipo
		//FARE UN DEFAULT?=!=!=!=!=!=

		switch(reply){
		case(OK):{
			newMessage(MSG_OK, NULL, &reply_msg, 0);
			sendMessage(client_sock_fd, &reply_msg);
			break;
		}

		case(WR_START):{
			newMessage(MSG_NO, WRONG_START, &reply_msg, strlen(reply_msg.buffer));
			sendMessage(client_sock_fd, &reply_msg);
			break;
		}

		case(WR_DEST):{
			newMessage(MSG_NO, WRONG_DEST, &reply_msg, strlen(reply_msg.buffer));
			sendMessage(client_sock_fd, &reply_msg);
			break;
		}

		case(SERVER_ERR):{
			newMessage(MSG_NO, SERVER_ERR, &reply_msg, strlen(reply_msg.buffer));
			sendMessage(client_sock_fd, &reply_msg);
			break;
		}
		}

	/*Non serve liberare il buffer del reply msg, il campo buffer non punta a stringhe allocate*/

	}

	//mah
	pthread_exit(NULL);

}

//FARE LE FREE DI TUTTO ALLA FINE

/**
 *
 * @param username
 * @param password
 * @return 0 OK, 1 PWD ERRATA, 2 GIA' CONNESSO
 */
int user_check(char *username, char *password, user **u) {
	user_list_node *uln;
	user_list_node *new;
	uln = user_list;


	//si scorre la lista
	printf("USER CHECK name !%s! pass !%s!\n", username, password);
	while (uln) {
		if (uln->user == NULL) {
			printf("primo utente\n"); // FA SCHIFO RITORNARCI SOPRA
			uln->user = malloc(sizeof(user));
			if(uln->user == NULL){
				perror("Errore di allocazione memoria");
				//funzione di chiusura?!
				return(-1);
			}
			uln->user->name = strdup(username);
			if(uln->user->name == NULL){
				perror("Errore di allocazione memoria");
				//funzione di chiusura?!
				return(-1);
			}
			uln->user->pwd = strdup(password);
			if(uln->user->pwd == NULL){
				perror("Errore di allocazione memoria");
				//funzione di chiusura?!
				return(-1);
			}
			uln->user->pending = 0;
			*u = uln->user;
			return (LOGIN_OK);
			//break;
		}
		printf("AVANZA\n");
		/*Con questi controlli si autentica un utente già registrato, ma disconnesso*/
		if (strcmp(uln->user->name, username) == 0) {
			printf("dopo strcmp name\n");
			if (strcmp(uln->user->pwd, password) == 0) {
				printf("dopo strcmp pwd\n");
				if (uln->user->sfd == 0) {
					*u = uln->user; //warning ma funziona...
					return (LOGIN_OK);

				} else
					return (ALREADY_CONN);
			} else
				return (WRONG_PWD);
		}
		uln = uln->next; //warning ma funziona...

	}
	printf("NUOVO UTENTE\n");

	/*Nuovo utente*/
//inserimento in coda
//fare altro codice per inserimento nuovo utente?
	uln = malloc(sizeof(user_list_node));
	if(uln == NULL){
		perror("Errore di allocazione memoria");
		//funzione di chiusura?!
		return(-1);
	}
	uln->next = NULL;
	uln->user = malloc(sizeof(user));
	uln->user->name = strdup(username);
	uln->user->pwd = strdup(password);
	uln->user->pending = 0;
	*u = uln->user;
	return (LOGIN_OK);
}


/**
 *
 * @param uln
 * @return
 */
user* new_user(user_list_node *uln, char *username, char *password){

	user_list_node *new_node = NULL;
	new_node = malloc(sizeof(user_list_node));
	if (new_node == NULL){
		perror("Errore di allocazione memoria");
		//funzione di chiusura?!
		return(NULL);
	}
	new_node->next = NULL;
	new_node->user = malloc(sizeof(user));
	if (new_node->user == NULL){
		perror("Errore di allocazione memoria");
		//funzione di chiusura?!
		return(NULL);
	}
	new_node->user->name = strdup(username);
	if (new_node->user->name == NULL){
		perror("Errore di allocazione memoria");
		//funzione di chiusura?!
		return(NULL);
	}
	new_node->user->pwd = strdup(password);
	if (new_node->user->pwd == NULL){
		perror("Errore di allocazione memoria");
		//funzione di chiusura?!
		return(NULL);
	}
	new_node->user->sfd = 0;
	new_node->user->tid = NULL;
	uln->next = new_node;
	return(new_node->user);
}






/**
 * Funzione di chiusura thread:
 * se la chiusura è dovuta all'uscita del client si occupa di mettere il corrispondente utente offline nella lista.
 * Chiude il socket.
 */

void worker_stop() {
	printf("WORKER STOP\n");
	if (pending_signal == 0) {
		pthread_mutex_lock(&users_mtx);
		printf("workerstop azzera l'utente\n");
		current_user->sfd = 0;
		//current_user->tid = 0;
		pthread_mutex_unlock(&users_mtx);
	}

	closeSocket(client_sock_fd); //mmmm
	pthread_exit(NULL);
}
/**
 *
 * @param buffer
 * @return 0 OK -1 ERRORE SERVER 1 ERRORE FROM 2 ERRORE TO
 *
 *
 */
int offer(char *buffer) {
	char source[LLABEL];
	char dest[LLABEL];
	int seats = 0;
	unsigned int from;
	unsigned int to;
	offer_t *off;
	printf("WORKER OFFER\n");
	sscanf(buffer,"%[^':']:%[^':']:%d",source,dest,&seats);//ec!!!!!!!
	printf("offer !%s! !%s! !%d!\n",source,dest,seats);
	from = is_node(map, source);
	if(from == -1){
		printf("nodo FROM sbagliato\n");
		return(WR_START);
	}
	to = is_node(map, dest);
	if(to == -1){
		printf("nodo TO sbagliato\n");
		return(WR_DEST);
	}
	off = malloc(sizeof(offer_t));
	if(off == NULL){
		return(SERVER_ERR);
	}
	off->from = from;
	off->to = to;
	off->provider = current_user;
	off->seats = seats;
	//inserimento nella coda offerte



	//PRIMA LA FREE DEL BUFFEEEEEERRRRRR
	//oppure un codice di risultato passato al worker start che agisce di conseguenza spedendo al client msg corrispondente
	//allocare staticamente i buffer per i messaggi standard di errre tipo partenza non presente, dest non presente etc
	if(offers == NULL){
	printf("offers nULL\n");

	}
	if(enqueue(offers,(void *)off) == -1){
		printf("ERRORE ENQUEUE\n");
		return(SERVER_ERR);
	}
	printf("WORKER: offerta messa in coda\n");
	return(OK);
}

/**
 *
 * @param buffer
 * @return
 */
int request(char *buffer) {

	char source[LLABEL];
	char dest[LLABEL];
	unsigned int from;
	unsigned int to;
	request_t *req;

	printf("WORKER REQUEST !%s!\n",buffer);
	sscanf(buffer,"%[^':']:%[^':']",source,dest);//ec!!!!!!!
	printf("offer !%s! !%s!\n",source,dest);

	from = is_node(map, source);
	if(from == -1){
		return(WR_START);
	}
	to = is_node(map, dest);
	if(to == -1){
		return(WR_DEST);
	}
	printf("request controllata/n");

	req = malloc(sizeof(request_t));
	if(req == NULL){
		return(SERVER_ERR);
	}
	req->from = from;
	req->to = to;
	req->client = current_user;


	if(enqueue(requests,(void *)req) == -1){
		printf("ERRORE ENQUEUE\n");
		return(SERVER_ERR);
	}
	printf("WORKER: richiesta messa in coda\n");
	current_user->pending++;
	return(OK);

}



/*Gestori dei segnali*/
static void worker_sigpipe_handler(int signum) {
	sgpipe = 1;
}


